﻿/*
 * Created by SharpDevelop.
 * User: Corrado Mio
 * Date: 31/05/2012
 * Time: 23:33
 * 
 * 
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;

using log4net;

namespace CSharpExt.Threading
{
    /// <summary>
    /// La classe ha il compito di create Thread ma aggiungendo alcune funzionalita':
    /// 
    /// - tiene traccia dei thread in esecuzione
    /// - protegge ogni thread dalle eccezioni non gestite
    /// - aggiunge il supporto a Runnable
    /// 
    /// Nota: ThreadGroup e' un'implementazione piu' intelligente, perche' permette di
    ///     tracciare solo i thread di cui si e' interessati, e non TUTTI i thread
    ///     dell'applicazione
    /// 
    /// </summary>
    public static class Threads
    {
        #region Private Data
        
        private static Dictionary<int, Thread> _runningThreads = new Dictionary<int, Thread>();
        
        #endregion
        
        #region Create
        
        // -- Action()
        
        public static Thread Create(Action callback)
        {
            return Create(null, callback);
        }
        
        public static Thread Create(String n, Action callback)
        {
            n = ThreadUtils.ComposeName(n, "Thread.{0}.{1}", callback);
            
            Thread thread = new Thread(
                () =>
                {
                    try
                    {
                        enterThread();
                        
                        callback();
                    }
                    catch (ThreadAbortException)
                    {
                        LogManager.GetLogger("Thread").Error("Thread Aborted");
                    }
                    catch (Exception e)
                    {
                        LogManager.GetLogger("Thread").Error(e, e);
                    }
                    finally
                    {
                        exitThread();
                    }
                })
            {
                Name = n
            };
            
            return thread;
        }
        
        
        // -- Action(Object)
        
        public static Thread Create(Action<Object> callbackArg)
        {
            return Create(null, callbackArg);
        }

        public static Thread Create(String n, Action<Object> callbackArg)
        {
            n = ThreadUtils.ComposeName(n, "Thread.{0}.{1}", callbackArg);
            
            Thread thread = new Thread(
                (arg) =>
                {
                    try
                    {
                        enterThread();
                        
                        callbackArg(arg);
                    }
                    catch (ThreadAbortException)
                    {
                        LogManager.GetLogger("Thread").Error("Thread Aborted");
                    }
                    catch (Exception e)
                    {
                        LogManager.GetLogger("Thread").Error(e, e);
                    }
                    finally
                    {
                        exitThread();
                    }
                })
            {
                Name = n
            };
            
            return thread;
        }
        
        
        // -- Runnable
        
        public static Thread Create(Runnable runnable)
        {
            return Create(null, runnable);
        }
        
        public static Thread Create(String n, Runnable runnable)
        {
            n = ThreadUtils.ComposeName(n, "Thread.{0}", runnable);
            
            return Create(n, () => { runnable.Run(); });
        }
        
        #endregion
        
        #region Advanced Informations
        
        public static int GetRunningThreads()
        {
            return _runningThreads.Count;
        }
        
        public static Thread[] RunningThreads
        {
            get
            {
                lock (_runningThreads)
                {
                    return _runningThreads.Values.ToArray();
                }
            }
        }
 
        public static Thread CurrentThread
        {
            get { return Thread.CurrentThread; }
        }
        
        #endregion
        
        #region Implementation
        
        private static void enterThread()
        {
            lock (_runningThreads)
            {
                Thread currentThread = Thread.CurrentThread;
                _runningThreads[currentThread.ManagedThreadId] = currentThread;
            }
        }
        
        private static void exitThread()
        {
            lock (_runningThreads)
            {
                Thread currentThread = Thread.CurrentThread;
                _runningThreads.Remove(currentThread.ManagedThreadId);
            }
        }
        
        #endregion
    }
}
